/*
 * @file: drag
 * @author: yangfan
 * @Create Time: 2016-06-16 16:41:27
 * 添加注释，修改点击移动 bug
 */
fangimjs.define('util/drag/1.0.0/drag', ['jquery'], function (require) {
    'use strict';
    var $ = require('jquery');

    var $window = $(window);
    var $document = $(document);
    var isTouch = 'createTouch' in document;
    var html = document.documentElement;
    var isIE6 = !('minWidth' in html.style);
    // 离开浏览器事件
    var isLosecapture = !isIE6 && 'onlosecapture' in html;
    // 收集鼠标事件
    var isSetCapture = 'setCapture' in html;

    // 定义触发拖动事件类型
    var types = {
        start: isTouch ? 'touchstart' : 'mousedown',
        over: isTouch ? 'touchmove' : 'mousemove',
        end: isTouch ? 'touchend' : 'mouseup'
    };

    // event 参数兼容
    var getEvent = isTouch ? function (event) {
        if (!event.touches) {
            event = event.originalEvent.touches.item(0);
        }
        return event;
    } : function (event) {
        return event;
    };

    // 初始化事件类型，默认定义事件回调为空函数，改变作用域
    var DragEvent = function () {
        this.start = $.proxy(this.start, this);
        this.over = $.proxy(this.over, this);
        this.end = $.proxy(this.end, this);
        this.onstart = this.onover = this.onend = $.noop;
    };

    DragEvent.types = types;

    DragEvent.prototype = {
        // 定义开始拖动事件
        start: function (event) {
            event = this.startFix(event);

            $document
                .on(types.over, this.over)
                .on(types.end, this.end);

            this.onstart(event);
            return false;
        },
        // 定义拖动中
        over: function (event) {
            event = this.overFix(event);
            this.onover(event);
            return false;
        },
        // 定义拖动结束事件，卸载拖动中和拖动结束事件
        end: function (event) {
            event = this.endFix(event);

            $document
                .off(types.over, this.over)
                .off(types.end, this.end);

            this.onend(event);
            return false;
        },
        /**
         * 开始拖动事件 evnet 参数兼容处理
         * selectstart 默认 return false 屏蔽选择
         * dblclick 默认执行拖动结束事件
         * 开启捕获鼠标事件
         */
        startFix: function (event) {
            event = getEvent(event);

            this.target = $(event.target);
            this.selectstart = function () {
                return false;
            };

            $document
                .on('selectstart', this.selectstart)
                .on('dblclick', this.end);

            if (isLosecapture) {
                this.target.on('losecapture', this.end);
            } else {
                $window.on('blur', this.end);
            }

            if (isSetCapture) {
                this.target[0].setCapture();
            }

            return event;
        },
        overFix: function (event) {
            event = getEvent(event);
            return event;
        },
        /**
         * 结束拖动事件 evnet 参数兼容处理
         * selectstart 默认 return false 屏蔽选择，在 startFix 中定义
         * 屏蔽捕获鼠标事件
         */
        endFix: function (event) {
            event = getEvent(event);

            $document
                .off('selectstart', this.selectstart)
                .off('dblclick', this.end);

            if (isLosecapture) {
                this.target.off('losecapture', this.end);
            } else {
                $window.off('blur', this.end);
            }

            if (isSetCapture) {
                this.target[0].releaseCapture();
            }

            return event;
        }

    };


    /**
     * 启动拖拽
     * @param   {HTMLElement}   被拖拽的元素
     * @param   {Event} 触发拖拽的事件对象。可选，若无则监听 elem 的按下事件启动
     * add 判断 elem 是否为 jq 对象
     */
    DragEvent.create = function (pElem, event, options) {
        var elem = pElem[0] ? $(pElem)[0] : pElem;
        if (!elem) {
            return false;
        }
        var $elem = $(elem);
        var width = $elem.width(),
            height = $elem.height();

        var dragEvent = new DragEvent();
        // 'touchstart' or 'mousedown' 触发拖动
        var startType = DragEvent.types.start;
        // 保留第一个类名，不以空开始
        var className = elem.className.replace(/^\s|\s.*/g, '') + '-drag-start';
        // 拖动范围
        var minX, minY, maxX, maxY;
        var isFixed = $elem.css('position') === 'fixed';

        // 定义拖动回调事件，this 为 elem
        var api = {
            onstart: function (event, left, top) {
                $elem.css({ width: width, height: height, left: left, top: top });
                options.onStart && options.onStart({
                    target: elem,
                    event: event,
                    left: left,
                    top: top,
                    width: width,
                    height: height
                });
            },
            onover: $.noop,
            // fix bug 源码在拖动结束时使用 right,bottom 定位会有单击移动的情况
            onend: function (event, left, top) {
                $elem.css({ width: width, height: height, left: left, top: top });
                options.onEnd && options.onEnd({
                    target: elem,
                    event: event,
                    left: left,
                    top: top,
                    width: width,
                    height: height
                });
            },
            // 卸载开始拖动事件
            off: function () {
                $elem.off(startType, dragEvent.start);
            }
        };

        // 开始拖动回调，计算移动边界，初始鼠标位置/窗口位置
        dragEvent.onstart = function (event) {
            var dsl = $document.scrollLeft(),
                dst = $document.scrollTop(),
                offset = $elem.offset();

            minX = 0;
            minY = 0;
            maxX = isFixed ? $window.width() - width + minX : $document.width() - width;
            maxY = isFixed ? $window.height() - height + minY : $document.height() - height;

            var left = this.startLeft = isFixed ? offset.left - dsl : offset.left,
                top = this.startTop = isFixed ? offset.top - dst : offset.top;

            this.clientX = event.clientX;
            this.clientY = event.clientY;

            $elem.addClass(className);
            api.onstart.call(elem, event, left, top);

        };

        // 拖动中回调，计算移动位置
        dragEvent.onover = function (event) {
            var left = event.clientX - this.clientX + this.startLeft,
                top = event.clientY - this.clientY + this.startTop;

            left = Math.max(minX, Math.min(maxX, left));
            top = Math.max(minY, Math.min(maxY, top));

            var style = elem.style;
            style.left = left + 'px';
            style.top = top + 'px';

            api.onover.call(elem, event, left, top);
        };

        // 拖动结束回调，其实无需再取元素位置。
        dragEvent.onend = function (event) {
            var dsl = $document.scrollLeft(),
                dst = $document.scrollTop(),
                offset = $elem.offset();

            var left = isFixed ? offset.left - dsl : offset.left,
                top = isFixed ? offset.top - dst : offset.top;

            // var position = $elem.position();
            // var left = position.left,
            //     top = position.top;

            $elem.removeClass(className);
            api.onend.call(elem, event, left, top);
        };

        if (event) {
            dragEvent.start(event);
        } else {
            $elem.on(startType, dragEvent.start);
        }

        return api;
    };

    return DragEvent;
});
